# This Python file uses the following encoding: utf-8
import sys
from threading import Thread

from PySide6 import QtWidgets
from pyqtgraph.Qt import  QtCore
from random import randint
from PySide6.QtCore import QThread, Signal, QObject

from PySide6.QtWidgets import QApplication, QMainWindow, QTextBrowser
import pyqtgraph as pg

import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_tcp, hooks

# Important:
# You need to run the following command to generate the ui_form.py file
#     pyside6-uic form.ui -o ui_form.py, or
#     pyside2-uic form.ui -o ui_form.py
from ui_form import Ui_MainWindow

import sqlite3

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        self.arrs = [[], [], [], [], [], []]

        super().__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.ui.pushButton.clicked.connect(self.setState)

        # 创建 PlotWidget 对象
        self.pw = pg.PlotWidget()

        # 设置图表标题
        self.pw.setTitle("tcp slave 图",
                         color='#008080',
                         size='12pt')

        # 设置上下左右的label
        self.pw.setLabel("left", "值")
        self.pw.setLabel("bottom", "次")

        # 设置Y轴 刻度 范围
        self.pw.setXRange(min=0, max=10, padding=1)

        # 显示表格线
        self.pw.showGrid(x=True, y=True)

        # 背景色改为白色
        self.pw.setBackground('w')

        # 设置Y轴 刻度 范围
        self.pw.setYRange(min=0,  # 最小值
                          max=50)  # 最大值

        # 居中显示 PlotWidget
        # self.setCentralWidget(self.pw)
        self.ui.widget.setLayout(QtWidgets.QVBoxLayout())
        self.ui.widget.layout().addWidget(self.pw)

        # 实时显示应该获取 PlotDataItem对象, 调用其setData方法，
        # 这样只重新plot该曲线，性能更高
        self.curves = [[], [], [], [], [], []]
        self.curves[0] = self.pw.plot(
            pen=pg.mkPen('#FF0000', width=1)
        )
        self.curves[1] = self.pw.plot(
            pen=pg.mkPen('#00FF00', width=1)
        )
        self.curves[2] = self.pw.plot(
            pen=pg.mkPen('#0000FF', width=1)
        )
        self.curves[3] = self.pw.plot(
            pen=pg.mkPen('#FFF000', width=1)
        )
        self.curves[4] = self.pw.plot(
            pen=pg.mkPen('#87CEEB', width=1)
        )
        self.curves[5] = self.pw.plot(
            pen=pg.mkPen('#C8A2C8', width=1)
        )

        self.i = 0
        self.x = []  # x轴的值
        self.y = []  # y轴的值

        # 启动定时器，每隔1秒通知刷新一次数据
        # self.timer = QtCore.QTimer()
        # self.timer.timeout.connect(self.updateData)
        # self.timer.start(1000)

    def initSql(self):
        # 连接到SQLite数据库
        # 如果数据库不存在，则会自动创建
        self.sqlconn = sqlite3.connect('data_storage.db')

        # 创建一个Cursor对象，用于执行SQL命令
        self.cursor = self.sqlconn.cursor()

        # 创建data_records表
        self.cursor.execute('''
                CREATE TABLE IF NOT EXISTS data_records (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    data BLOB,
                    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
                )
                ''')

        print("Table created successfully.")

        # self.cursor.execute("SELECT * FROM data_records")
        # rows = self.cursor.fetchall()
        # for row in rows:
        #     print(row)
        #
        # # 查询数据
        # self.cursor.execute("SELECT data FROM data_records")
        # rows = self.cursor.fetchall()
        #
        # for row in rows:
        #     # row[0] 是 id，row[1] 是 data 字段的值
        #     print(f"Data: {row}")
        #     self.updateData2(row[0], False)

    def updateSql(self, ba:bytearray):
        # 插入一条记录
        self.cursor.execute("INSERT INTO data_records (data) VALUES (?)", (ba,))

        # 提交事务
        self.sqlconn.commit()
        print("self.Data inserted successfully.")

    def checkBoxChanged(self, idx, state):
        index = idx - 1
        if(state):
            self.curves[index].setData(self.x, self.arrs[index])
        else:
            self.curves[index].clear()

        pass

    def setState(self):
        if self.ui.pushButton.text() == "开启":
            self.ui.pushButton.setText("关闭")
            self.slave = TcpSlave()
            self.slave.init(self)
            self.slave.start()
        else:
            self.ui.pushButton.setText("开启")
            self.slave.stop()

    def updateData(self):
        self.i += 1
        self.x.append(self.i)
        # 创建随机温度值
        self.y.append(randint(10,30))

        # plot data: x, y values
        self.curve.setData(self.x,self.y)

    def updateData2(self, ba:bytearray, needLog:bool = True):
        self.i += 1
        self.x.append(self.i)
        # self.arrs
        baarr = list(ba)
        ba6 = baarr[8:]
        # print('updateData2: {}'.format(ba6))

        result = [int.from_bytes(ba6[i:i + 2], byteorder='big') for i in range(0, len(ba6), 2)]
        print("res::{0}".format(result))

        if needLog:
            self.ui.logInfo.append('写多个寄存器: {0}'.format(result))

        for index, value in enumerate(result):
            if(index >= len(self.arrs)):
                break
            self.arrs[index].append(value)
            self.curves[index].setData(self.x, self.arrs[index])
            # print(index, value)

        print('arrs: {0}'.format(self.arrs))
        pass


# 自定义信号源对象类型，一定要继承自 QObject
class MySignals(QObject):

    # 定义一种信号，两个参数 类型分别是： QTextBrowser 和 字符串
    # 调用 emit方法 发信号时，传入参数 必须是这里指定的 参数类型
    updateSql = Signal(bytearray)

    # 还可以定义其他种类的信号
    update_table = Signal(str)

# 实例化
global_ms = MySignals()

class TcpSlave(QThread):
    def init(self, mainwindo: MainWindow):
        self.ui = mainwindo

        global_ms.updateSql.connect(self.ui.updateSql)

    def log(self, str:str):
        if(str == '清空'):
            self.ui.ui.logInfo.clear()
        else:
            self.ui.ui.logInfo.append(str)

    def stop(self):
        self.server.stop()
        self.log('服务已关闭')

    def run(self):
        # 配置日志
        self.server = modbus_tcp.TcpServer()
        self.server.start()

        slave_1 = self.server.add_slave(1)
        slave_1.add_block('0', cst.HOLDING_REGISTERS, 0, 10)

        self.log('服务已开启')

        def on_connect(args):
            pername = args[2]
            self.log('已连接至{0}:{1}'.format(pername[0], pername[1]))
            print("on_connect {0}".format(args[2]))

        def on_disconnect(args):
            pername = args[1].getpeername()
            self.log('已断开连接{0}:{1}'.format(pername[0], pername[1]))

        def after_recv(args):
            # bytearray(b'\x10\x00d\x00\x0c\x18\x00\x00 \x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\t\x00\n\x00\x0b')
            # 1.事物标识符（2byte）: 就是一个递增的数字，每次发送消息递增一下即可，尽量不重复，因为占用2个字节，所以范围是：0~65535
            # 2.协议标识符（2byte）: 这个默认给固定值0，表示modbus协议
            # 3.长度（2byte）: 等于协议标识符的长度2+协议数据单元PDU的长度（不固定）
            # 4.单元标识符（1byte）: 这个就是从机地址，就是slave id
            # 5.功能码（1byte）: Modbus规定了多个功能，那么为了方便的使用这些功能，我们给每个功能都设定一个功能码，就是上边说的，你要对从机做什么操作，那么就在这里设定好，从机读取到这个数据就知道要做什么啦。
            # 6.数据（nbyte）: 要写入的实际数据，字节数等于前面的字节计数字段
            # if(args == None):
            #     self.log("收到错误信息")
            # else:
            #     pername = args[1].getpeername()
            #     data = args[2]
            #     self.log('来自{0}:{1}的数据: {2}'.format(pername[0], pername[1], data))
            #     slave = self.server.get_slave(1)
            #     values = slave.get_values('0', 0, 10)
            #     print('value: {0}'.format(values))
            print('recv {0}'.format(args))

        def before_send(args):
            print('send {0}'.format(args))

        def on_error(args):
            self.log('error: {0}'.format(args))
            print('error {0}'.format(args))

        def handle_write_multiple_registers_request(args):
            # bytearray(b'\x10\x00d\x00\x0c\x18\x00\x00 \x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\t\x00\n\x00\x0b')
            # 1.单元标识符（UnitIdentifier）: 通常在ModbusCP中使用，对于RTU会直接开始于地址。
            # 2.功能码（Function Code）: 对于写多个寄存器，这是0x10 "写多个寄存器"请求的功能码为0x10
            # 3.起始地址（Starting Address）: 请求开始写入的第一个寄存器的地址，2字节。
            # 4.寄存器数量（Quantity of Registers）: 要写入的寄存器的数量，1字节。
            # 5.字节计数（Byte Count）: 随后的写入数据的字节数，1字节。
            # 6.数据（Data）: 要写入的实际数据，字节数等于前面的字节计数字段
            print('handle_write_multiple_registers_request {0}'.format(args))
            pdu = args[1]
            self.ui.updateData2(pdu)
            global_ms.updateSql.emit(pdu)


        # modbus_tcp.TcpServer.on_connect((server, client, address))
        # modbus_tcp.TcpServer.on_disconnect((server, sock))
        # modbus_tcp.TcpServer.after_recv((server, sock, request)) returns modified request or None
        # modbus_tcp.TcpServer.before_send((server, sock, response)) returns modified request or None
        # modbus_tcp.TcpServer.on_error((server, sock, excpt))

        # modbus.Slave.handle_write_multiple_registers_request((slave, request_pdu))
        # modbus.Slave.handle_write_single_register_request((slave, request_pdu))


        hooks.install_hook("modbus_tcp.TcpServer.on_connect", on_connect)
        hooks.install_hook("modbus_tcp.TcpServer.on_disconnect", on_disconnect)
        hooks.install_hook("modbus_tcp.TcpServer.after_recv", after_recv)
        # hooks.install_hook("modbus_tcp.TcpServer.before_send", before_send)
        hooks.install_hook("modbus_tcp.TcpServer.on_error", on_error)
        hooks.install_hook("modbus.Slave.handle_write_multiple_registers_request", handle_write_multiple_registers_request)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    widget = MainWindow()

    widget.show()
    sys.exit(app.exec())
